<?php

namespace src;

use Closure;
use Exception;
use Throwable;

class Pipeline
{
    protected $container;
    protected $method = 'handle';
    protected $passable;
    protected $pipes = [];

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function send($passable)
    {
        $this->passable = $passable;
        return $this;
    }

    public function through($pipes)
    {
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();
        return $this;
    }

    public function then(Closure $destination)
    {
        $pipeline = array_reduce(array_reverse($this->pipes()),
            $this->carry(),
            $this->prepareDestination($destination)
        );
        return $pipeline($this->passable);
    }

    protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            try {
                return $destination($passable);
            } catch (Exception $e) {
                throw $e;
            }
        };
    }

    protected function pipes()
    {
        return $this->pipes ?? [];
    }

    protected function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                if ($pipe instanceof Closure){
                    return $pipe($passable);
                }elseif (!is_object($pipe)){
                    $obj = $this->container->make($pipe);
                    if(!method_exists($obj, $this->method)){
                        throw new Exception('管道方法不存在');
                    }
                    return $obj->{$this->method}($passable, $stack);
                }
            };
        };
    }
}